// condition.c
// From ES2
// Modified by Xiang for XKX (12/15/95)
// Update by Doing Lu for HELL

#include <condition.h>

nosave string last_applyer_name;
nosave string last_applyer_id;

mapping cond_applyer;
mapping conditions;

mapping query_CONDITION()
{
        mapping m=([]);
        m["cond_applyer"] = cond_applyer;
        m["conditions"] = conditions;
        return m;
}

int set_CONDITION(mapping m)
{
        if( !mapp(m) )
                return 0;

        cond_applyer = m["cond_applyer"];
        conditions = m["conditions"];
        return 1;
}

string query_last_applyer_name()
{
        return last_applyer_name;
}

string query_last_applyer_id()
{
        return last_applyer_id;
}

// Get the object of condiction daemon
protected object get_cnd_object(string cnd)
{
        mixed err;
        object cnd_d;

        cnd_d = find_object(CONDITION_D(cnd));
        if( !cnd_d )
        {
                err = catch(cnd_d = load_object(CONDITION_D(cnd)));

                // If we failed to load the external condition daemon, remove
                // it before we stuff log files with error messages.

                if( err || !cnd_d ) {
                        log_file("condition.err",
                                sprintf("Failed to load condition daemon %s, removed from %O\n"
                                        "Error: %s\nCurrent conditions:%O\n",
                                        CONDITION_D(cnd), this_object(), err,
                                        conditions));
                }
        }

        return cnd_d;
}

// clear a condition of all the condition when cnd == 0
nomask void clear_condition(string cnd)
{
        if( !cnd ) {
                conditions = 0;
                cond_applyer = 0;
                last_applyer_name = 0;
                last_applyer_id = 0;
        } else if( conditions ) {
                map_delete(conditions, cnd);
                if( !sizeof(conditions) )
                        conditions = 0;

                if( cond_applyer ) {
                        map_delete(cond_applyer, cnd);
                        if( !sizeof(cond_applyer))
                                cond_applyer = 0;
                }
        }
}

// This function is called by heart_beat to update "continously active"
// conditions. These conditions will be saved as well. Because such type
// of conditions costs heart beat evaluation time, don't make player got
// too much this kind of conditions or you might got lots fo "Too long
// evaluation" error message in the log file.

nomask int update_condition()
{
        string *cnd;
        string *last_applyer;
        int i, flag, update_flag;
        object cnd_d;

        if( !mapp(conditions) ) return 0;
        if( !(i = sizeof(conditions)) ) {
                conditions = 0;
                cond_applyer = 0;
                return 0;
        }
        cnd = keys(conditions);
        update_flag = 0;
        while( i-- ) {

                // In order to not casue player lost heart beat occasionally while
                // calling external condition daemons, we take careful calling
                // convention here.

                cnd_d = get_cnd_object(cnd[i]);
                if( !cnd_d ) {
                        if( cnd[i] ) clear_condition(cnd[i]);
                        continue;
                }

                // We assume since the condition daemon is loaded successfully, the
                // calling on its update_condition() should success as well. Because
                // catch() is somewhat costly, so we don't attempt to catch possible
                // error from the call_other. It is condition daemon's reponsibility
                // that don't cause error in users's heart beat.
                // If condition daemon returns 0 (or update_condition() not defined),
                // we can just assume the condition expired and remove it.

                if( cond_applyer && (last_applyer = cond_applyer[cnd[i]]) ) {
                        last_applyer_id = last_applyer[0];
                        last_applyer_name = last_applyer[1];
                } else {
                        last_applyer_id = 0;
                        last_applyer_name = 0;
                }

                flag = call_other(cnd_d, "update_condition", this_object(), conditions[cnd[i]]);
                if( !conditions ) {
                        update_flag |= flag;
                        break;
                }
                if( !(flag & CND_CONTINUE) ) {
                        clear_condition(cnd[i]);
                }
                update_flag |= flag;
        }

        return update_flag;
}

// apply_condition()
//
// This function applies the specific condition to the character. Note we
// don't check if the condition already exist before setting condition info.
// It is condition giver's reponsibility to check if the condition should
// override the old one if same condition already exists.(Use query_condition
// to check)

nomask void apply_condition(string cnd, mixed info)
{
        object applyer;

        if( !stringp(cnd) ) cnd = "poison";  // 加上调试，cnd 丢失
        if( !stringp(cnd) ) {
                log_file("condition.err", sprintf("Error to apply condition(%O) from object(%O) with info(%O)\n",
                        cnd, previous_object(), info));
                return;
        }

        if( !mapp(conditions) ) {
                conditions = ([ cnd : info ]);
        } else
                conditions[cnd] = info;

        if( objectp(applyer = this_player()) && applyer != this_object() && userp(applyer) ) {
                if( !mapp(cond_applyer) )
                        cond_applyer = ([ cnd : ({ query("id", applyer), applyer->name(1) }) ]);
                else
                        cond_applyer[cnd] = ({ query("id", applyer), applyer->name(1) });
        }

        set_heart_beat(1);
}

// query_condition()
//
// This function returns info about the specific condition if any.

nomask mixed query_condition(string cnd)
{
        if( !cnd ) return conditions;

        if( !mapp(conditions) || undefinedp(conditions[cnd]) )
                return 0;
        return conditions[cnd];
}

// query_last_appler()
//
// This function returns last applyer id & name's mapping

nomask mixed query_last_applyer(string cnd)
{
        if( !cnd )
                return cond_applyer;
        else {
                if( !mapp(cond_applyer) )
                        return 0;

                return cond_applyer[cnd];
        }
}

// Query a conditon's name
nomask string query_condition_name(string cnd)
{
        object cnd_d;

        if( !(cnd_d = get_cnd_object(cnd)) )
        {
                log_file("condition.err", sprintf("%O query condition %s\n",
                        this_object(), CONDITION_D(cnd)));
                return 0;
        }

        return cnd_d->cnd_name();
}

// Dispel a condition
nomask int dispel_condition(object ob, string cnd)
{
        object cnd_d;

        if( !(cnd_d = get_cnd_object(cnd)) )
        {
                log_file("condition.err", sprintf("%O try to dispel %O %s\n",
                        ob, this_object(), CONDITION_D(cnd)));
                return 0;
        }

        return cnd_d->dispel(ob, this_object(), conditions[cnd]);
}

// Call the condition's do_effect funtion
nomask int affect_by(string cnd, mixed para)
{
        object cnd_d;
        int reduce, avoid, add;
        object me;

        if( !(cnd_d = get_cnd_object(cnd)) )
        {
                log_file("condition.err", sprintf("%O affect by %s:%O\n",
                        this_object(), CONDITION_D(cnd), para));
                return 0;
        }

        if( !para ) para = query_temp("para");

        avoid = this_object()->query_all_buff("avoid_poison");
        reduce = this_object()->query_all_buff("reduce_poison");

        if( random(100) < avoid ) return 0;

        if( objectp(me = find_player(para["id"])) )
        {
                if( query("special_skill/yaowang", me) )
                {
                        para["level"] = para["level"] + para["level"] / 5;
                        para["duration"] = para["duration"] + para["duration"] / 5;
                }
                add = me->query_all_buff("add_poison");
                add = add * (100 - query("reborn/times")*10) / 100;
                para["level"] = para["level"] * (100 + add) / 100;
                para["duration"] = para["duration"] * (100 + add) / 100;
        }

        if( objectp(me) && query("special_skill/nopoison", this_object()) )
        {
                avoid = ABILITY_D->check_ability(me, "powerup_poison", 2);
                if( random(100) >= avoid ) return 0;
        }

        // 提高抗毒效果
        if( reduce > 90 ) reduce = 90;
        para["level"] = para["level"] * (100 - reduce) / 100;
        para["duration"] = para["duration"] * (100 - reduce) / 100;

        return cnd_d->do_effect(this_object(), cnd, para);
}

